C와 C++에서의 포인터(Pointer)
**포인터(pointer)**는 C와 C++에서 매우 중요한 개념입니다. 포인터는 메모리를 직접 제어할 수 있게 해주며, 동적 메모리 할당, 함수 인자로 참조 전달, 효율적인 자료구조 구현 등에 필수적으로 사용됩니다.
1. 포인터란 무엇인가?
- 정의: 포인터는 "다른 변수의 메모리 주소를 저장하는 변수"입니다.
- 자료형 명시: 포인터의 타입은 자신이 가리키는 대상의 타입을 나타냅니다. (예:
int *,char *등)
예제:
int x = 42;
int *p = &x; // p는 x의 주소를 가리킴
*p은 "int를 가리키는 포인터"라고 읽습니다.&x는 변수 x의 주소를 반환합니다.
2. 기본 문법: 주소 & 역참조(dereferencing)
주소 연산자 &
- 변수의 주소(메모리 위치)를 가져옵니다.
예시:
int x = 10;
printf("%p\n", &x); // x의 주소 출력 (예: 0x7ffee07dc2ac)
역참조 연산자 *
- 포인터가 가리키는 주소의 실제 값을 읽거나 수정할 때 사용합니다.
예시:
int y = 20;
int *py = &y;
printf("%d\n", *py); // 출력: 20 (y의 값)
*py = 5; // y의 값이 5로 바뀜
3. 포인터의 종류
A. 기본 포인터(Basic Pointer)
int a = 100;
int *aptr = &a; // a의 주소를 저장
B. 널 포인터 (NULL Pointer)
- 어떤 것도 가리키지 않는 포인터
int *ptr = NULL; // 안전한 초기화
C. void 포인터 (Generic Pointer)
- 어떤 타입이든 가리킬 수 있으나, 역참조 시 형변환 필요
void *vptr;
int val = 12;
vptr = &val;
printf("%d\n", *(int*)vptr); // 형 변환 후 접근
D. 이중 포인터 (Pointer to Pointer)
int z = 50;
int *pz = &z;
int **ppz = &pz;
printf("%d\n", **ppz); // 출력: 50
4. 포인터 연산(Pointer Arithmetic)
- 포인터에 정수를 더하거나 빼면 자료형 크기 단위로 이동합니다 (byte 단위가 아님!)
예시:
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr; // arr[0]을 가리킴
p++; // 이제 arr[1]을 가리킴
printf("%d\n", *p); // 출력: 2
5. 배열과 포인터
- 배열 이름 자체는 배열의 첫 번째 요소의 포인터처럼 동작합니다.
int arr[3] = {10, 20, 30};
int *p = arr;
printf("%d %d\n", arr[1], *(p + 1)); // 둘 다 20 출력
6. 함수 포인터(Function Pointer)
- 포인터로 함수 주소를 저장하고 호출할 수 있음 (콜백, 이벤트 핸들러 등에서 사용)
int add(int a, int b) { return a + b; }
int (*fnptr)(int, int) = &add;
printf("%d\n", fnptr(2, 3)); // 출력: 5
7. 동적 메모리와 포인터
- C:
malloc/free - C++:
new/delete
C 예제:
int *p = (int*)malloc(sizeof(int));
*p = 42;
free(p);
C++ 예제:
int *p = new int(42);
delete p;
8. 구조체/클래스와 포인터
C에서 구조체 포인터 사용:
typedef struct {
int age;
} Person;
Person p1 = {25};
Person *pperson = &p1;
printf("%d\n", pperson->age); // 25
C++ 클래스에서:
class Dog {
public:
int age;
};
Dog d;
Dog *dp = &d;
dp->age = 5;
9. 자주 발생하는 실수 및 위험
- ❌ Dangling Pointer: 이미 해제된 메모리를 참조
- ❌ Memory Leak: 할당한 메모리를 해제하지 않음
- ❌ Uninitialized Pointer: 초기화 되지 않은 포인터 → 임의 접근
- ❌ 포인터 연산 오버플로우: 배열 범위 초과 등
10. C vs C++에서의 포인터 비교
| 항목 | C | C++ |
|---|---|---|
| 기본 선언 | int *p; |
int *p; |
| 널 포인터 사용 | NULL 또는 0 |
nullptr (C++11), NULL |
| 동적 메모리 할당 | malloc, free |
new, delete, 스마트 포인터 |
| 배열과 포인터 | 거의 동일 | C++ STL 사용 가능 (vector, 등) |
| 스마트 포인터 | 직접 코드 작성 필요 | unique_ptr, shared_ptr 등 있음 |
✅ 요약
- 포인터는 “메모리 주소를 저장하는 변수”
*= 역참조,&= 주소 추출- 정적/동적 메모리, 함수 주소, 구조체/클래스 멤버 접근, 배열 등 다양한 사용처
- 강력하지만 메모리 관리 어려움 ⇒ 꼭 초기화, 해제, 범위 체크 필요
- C++에서는 스마트 포인터로 메모리 관리할 수 있음